package service

import (
	"context"
	"errors"
	"gitee.com/jn-shao/common"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
	"strconv"

	"svcSrv/domain/model"
	"svcSrv/domain/repository"
	"svcSrv/proto/svcSrv"
)

// 这里是接口类型
type ISvcDataService interface {
	AddSvc(*model.Svc) (int64, error)
	DeleteSvc(int64) error
	UpdateSvc(*model.Svc) error
	FindSvcByID(int64) (*model.Svc, error)
	FindSvcList(Svc *model.Page) (int64, []model.Svc, error)
	CreateToK8s(*svcSrv.SvcInfo) error
	DeleteFromK8s(*model.Svc) error
	UpdateToK8s(*svcSrv.SvcInfo) error
}

type SvcDataService struct {
	// 注意：这里是 ISvcRepository 类型
	SvcRepository repository.ISvcRepository
	// 相当于kubectl与k8s交互
	K8sClientSet *kubernetes.Clientset
	// Service
	service *corev1.Service
}

// 创建
// 注意：返回值 ISvcDataService 接口类型
func NewSvcDataService(svcRepository repository.ISvcRepository, clientSet *kubernetes.Clientset) ISvcDataService {
	return &SvcDataService{
		SvcRepository: svcRepository,
		K8sClientSet:  clientSet,
		service:       &corev1.Service{},
	}
}

// 添加 Svc
func (p *SvcDataService) AddSvc(svc *model.Svc) (int64, error) {
	return p.SvcRepository.CreateSvc(svc)
}

// 删除 Svc
func (p *SvcDataService) DeleteSvc(svcID int64) error {
	return p.SvcRepository.DeleteSvcByID(svcID)
}

// 更新 Svc
func (p *SvcDataService) UpdateSvc(svc *model.Svc) error {
	return p.SvcRepository.UpdateSvc(svc)
}

// 单个 Svc 查找
func (p *SvcDataService) FindSvcByID(svcID int64) (*model.Svc, error) {
	return p.SvcRepository.FindSvcByID(svcID)
}

// 查找所有 Svc
func (p *SvcDataService) FindSvcList(page *model.Page) (int64, []model.Svc, error) {
	return p.SvcRepository.FindSvcList(page)
}

// 创建 Svc 到 k8s 中
func (p *SvcDataService) CreateToK8s(svcInfo *svcSrv.SvcInfo) error {
	p.setService(svcInfo)
	// 查看 services 是否存在
	if _, err := p.K8sClientSet.CoreV1().Services(svcInfo.SvcNamespace).Get(context.TODO(), svcInfo.SvcName, metav1.GetOptions{}); err != nil {
		// 不存在，创建 services
		if _, err = p.K8sClientSet.CoreV1().Services(svcInfo.SvcNamespace).Create(context.TODO(), p.service, metav1.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("创建成功")
		return nil

	} else {
		// 存在，可以写自己的业务逻辑
		common.Error("Service " + svcInfo.SvcName + "已经存在")
		return errors.New("Service " + svcInfo.SvcName + " 已经存在")
	}
}

// 删除 Svc
func (p *SvcDataService) DeleteFromK8s(svc *model.Svc) error {
	// 删除 service
	if err := p.K8sClientSet.CoreV1().Services(svc.SvcNamespace).Delete(context.TODO(), svc.SvcName, metav1.DeleteOptions{}); err != nil {
		common.Error(err)
		//写自己的业务逻辑
		return err
	}
	common.Info(svc.SvcName + " 删除成功")
	return nil
}

// 更新 Svc
func (p *SvcDataService) UpdateToK8s(svcInfo *svcSrv.SvcInfo) error {
	p.setService(svcInfo)
	// 查看 service 是否存在
	if service, err := p.K8sClientSet.CoreV1().Services(svcInfo.SvcNamespace).Get(context.TODO(), svcInfo.SvcName, metav1.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("Service " + svcInfo.SvcName + " 不存在请先创建")
	} else {
		// 如果存在
		p.service.SetResourceVersion(service.ResourceVersion)
		if _, err = p.K8sClientSet.CoreV1().Services(svcInfo.SvcNamespace).Update(context.TODO(), p.service, metav1.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
	}
	common.Info(svcInfo.SvcName + " 更新成功")
	return nil
}

func (p *SvcDataService) setService(svcInfo *svcSrv.SvcInfo) {
	service := &corev1.Service{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Service",
			APIVersion: "v1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      svcInfo.SvcName,
			Namespace: svcInfo.SvcNamespace,
			Labels: map[string]string{
				"app-name": svcInfo.SvcName,
				"author":   "SHao",
			},
			Annotations: map[string]string{
				"k8s/generated-by": "go-paas",
			},
		},
		//@TODO 采用ClusterIP模式,后续添加其他Service类型
		Spec: corev1.ServiceSpec{
			Ports: p.getSvcPort(svcInfo),
			Selector: map[string]string{
				"app-name": svcInfo.SvcPodName,
			},
			Type: "ClusterIP",
		},
	}
	p.service = service
}

func (p *SvcDataService) getSvcPort(svcInfo *svcSrv.SvcInfo) (servicePort []corev1.ServicePort) {
	for _, v := range svcInfo.SvcPort {
		servicePort = append(servicePort, corev1.ServicePort{
			Name:       "port-" + strconv.FormatInt(int64(v.SvcPort), 10),
			Protocol:   p.getProtocol(v.SvcPortProtocol),
			Port:       v.SvcPort,
			TargetPort: intstr.FromInt(int(v.SvcTargetPort)),
		})
	}
	return
}

func (p *SvcDataService) getProtocol(protocol string) corev1.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}
}
